<!DOCTYPE html>






<html class="theme-next muse use-motion" lang="zh-Hans">
<head><meta name="generator" content="Hexo 3.9.0">
  <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="theme-color" content="#222">







  <script src="/lib/pace/pace.min.js?v=1.0.2"></script>
  <link href="/lib/pace/pace-theme-minimal.min.css?v=1.0.2" rel="stylesheet">







<meta http-equiv="Cache-Control" content="no-transform">
<meta http-equiv="Cache-Control" content="no-siteapp">


















  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css">



















































<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css">

<link href="/css/main.css?v=5.1.3" rel="stylesheet" type="text/css">


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=5.1.3">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=5.1.3">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=5.1.3">


  <link rel="mask-icon" href="/images/logo.svg?v=5.1.3" color="#222">





  <meta name="keywords" content="Android, Java">










<meta name="description" content="哈哈~">
<meta name="keywords" content="zachaxy">
<meta property="og:type" content="website">
<meta property="og:title" content="zachaxy">
<meta property="og:url" content="https://zachaxy.github.io/page/7/index.html">
<meta property="og:site_name" content="zachaxy">
<meta property="og:description" content="哈哈~">
<meta property="og:locale" content="zh-Hans">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="zachaxy">
<meta name="twitter:description" content="哈哈~">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Muse',
    version: '5.1.3',
    sidebar: {"position":"right","display":"hide","offset":12,"b2t":false,"scrollpercent":true,"onmobile":true},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: 'undefined',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="https://zachaxy.github.io/page/7/">





  <title>zachaxy</title>









</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">






  <div class="container sidebar-position-right
  page-home">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">


    <div class="custom-logo-site-title">
      <a href="/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">zachaxy</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>

        <h1 class="site-subtitle" itemprop="description">纵情向前！</h1>

  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">



    <ul id="menu" class="menu">


        <li class="menu-item menu-item-home">
          <a href="/" rel="section">

              <i class="menu-item-icon fa fa-fw fa-home"></i> <br>

            首页
          </a>
        </li>


        <li class="menu-item menu-item-tags">
          <a href="/tags" rel="section">

              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br>

            标签
          </a>
        </li>


        <li class="menu-item menu-item-archives">
          <a href="/archives" rel="section">

              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br>

            归档
          </a>
        </li>


        <li class="menu-item menu-item-about">
          <a href="/about" rel="section">

              <i class="menu-item-icon fa fa-fw fa-user"></i> <br>

            关于
          </a>
        </li>



        <li class="menu-item menu-item-search">

            <a href="javascript:;" class="popup-trigger">


              <i class="menu-item-icon fa fa-search fa-fw"></i> <br>

            搜索
          </a>
        </li>

    </ul>



    <div class="site-search">

  <div class="popup search-popup local-search-popup">
  <div class="local-search-header clearfix">
    <span class="search-icon">
      <i class="fa fa-search"></i>
    </span>
    <span class="popup-btn-close">
      <i class="fa fa-times-circle"></i>
    </span>
    <div class="local-search-input-wrapper">
      <input autocomplete="off" placeholder="搜索..." spellcheck="false" type="text" id="local-search-input">
    </div>
  </div>
  <div id="local-search-result"></div>
</div>



    </div>

</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">

  <section id="posts" class="posts-expand">









  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">



  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://zachaxy.github.io/2017/10/20/Maven简介/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="zachaxy">
      <meta itemprop="description" content>
      <meta itemprop="image" content="/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="zachaxy">
    </span>


      <header class="post-header">



          <h2 class="post-title" itemprop="name headline">

                <a class="post-title-link" href="/2017/10/20/Maven简介/" itemprop="url">Maven简介</a></h2>


        <div class="post-meta">
          <span class="post-time">

              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>

                <span class="post-meta-item-text">发表于</span>

              <time title="创建于" itemprop="dateCreated datePublished" datetime="2017-10-20T19:57:18+08:00">
                2017-10-20
              </time>





          </span>













            <div class="post-wordcount">


                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>

                  <span class="post-meta-item-text">字数统计&#58;</span>

                <span title="字数统计">
                  4,791
                </span>





            </div>




        </div>
      </header>





    <div class="post-body" itemprop="articleBody">






          <p>最近在看 Gradle 相关的知识，看到介绍 Gradle 的开篇总是会拿来和 Maven 做对比，于是就好奇了解下 Maven 的使用，本篇文章只是对 Maven 很浅显的一个介绍，知道 Maven 是怎么回事，并不会对 Maven 做深入的探讨，因为我们的重点还是 Gradle。</p>
<p>Ant，Maven，Gradle 都是优秀的项目管理工具，做 Android 开发时应该都知道整个项目是用 gradle 进行管理的，平时用的最多的也就是往 gradle 配置文件中添加第三方库的依赖，仅此而已。接着我们直接点击了 run 的按钮等最终结果的输出了，而这些项目管理工具到底管理的是什么呢？我们不去看官方的话语，这里用大白话解释下，所谓项目管理，可以想象一个很大的项目，模块无穷多，这时就遇到问题了。</p>
          <!--noindex-->
          <div class="post-button text-center">
            <a class="btn" href="/2017/10/20/Maven简介/#more" rel="contents">
              阅读全文 &raquo;
            </a>
          </div>
          <!--/noindex-->


    </div>










    <footer class="post-footer">








        <div class="post-eof"></div>

    </footer>
  </div>



  </article>











  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">



  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://zachaxy.github.io/2017/10/17/AsyncTask详解/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="zachaxy">
      <meta itemprop="description" content>
      <meta itemprop="image" content="/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="zachaxy">
    </span>


      <header class="post-header">



          <h2 class="post-title" itemprop="name headline">

                <a class="post-title-link" href="/2017/10/17/AsyncTask详解/" itemprop="url">AsyncTask详解</a></h2>


        <div class="post-meta">
          <span class="post-time">

              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>

                <span class="post-meta-item-text">发表于</span>

              <time title="创建于" itemprop="dateCreated datePublished" datetime="2017-10-17T19:49:18+08:00">
                2017-10-17
              </time>





          </span>













            <div class="post-wordcount">


                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>

                  <span class="post-meta-item-text">字数统计&#58;</span>

                <span title="字数统计">
                  3,627
                </span>





            </div>




        </div>
      </header>





    <div class="post-body" itemprop="articleBody">







            <h2 id="AsyncTask详解"><a href="#AsyncTask详解" class="headerlink" title="AsyncTask详解"></a>AsyncTask详解</h2><h3 id="AsyncTask中的参数"><a href="#AsyncTask中的参数" class="headerlink" title="AsyncTask中的参数"></a>AsyncTask中的参数</h3><p>AsyncTask是一个抽象类,如果我们想使用它就要自定义一个子类去继承它。继成时我们可以为<br>AsyncTask制定三个泛型参数:</p>
<ul>
<li>Params:调用AsyncTask的excute方法时传入的参数类型，如果该方法传入了多个不同类型的参数，那么就定义为Object。</li>
<li>Progress:执行AsyncTask时如果需要在前台显示进度条,使用该类型作为进度的单位</li>
<li>Result:当AsyncTask结束后,如果需要对结果进行返回,该类型作为返回值的类型</li>
</ul>
<p>如果某一个类型不需要传递具体的参数,那么对应的泛型参数用<code>Void</code>代替</p>
<h3 id="AsyncTask中一些重要的方法"><a href="#AsyncTask中一些重要的方法" class="headerlink" title="AsyncTask中一些重要的方法"></a>AsyncTask中一些重要的方法</h3><ul>
<li>void onPreExecute():在主线程执行,用于进行一些界面上的初始化操作,eg:显示一个进度条对话框等</li>
</ul>
<ul>
<li>Result doInBackground(Params … params):在子线程中执行,用于执行耗时任务,相当于Thread中的run()方法</li>
<li>void onProgressUpdate(Progress… values):在主线程中执行,前提是在<code>doInBackground</code>方法中执行了publishProgress(Progress… values),由该方法自动调用<code>onProgressUpdate</code>,可以界面上实时显示进度</li>
<li>void onPostExecute(Result res),在子线程中执行,<code>doInBackground</code>方法执行结束后,将返回值传给该方法,负责任务结束后的工作</li>
<li>void publishProgress(Progress… values),一般在<code>doInBackground</code>方法中调用(非必须),将当前进度传递出来,一旦执行了该方法,那么将自动回调onProgressUpdate方法,并将当前进度作为参数传递给它.</li>
<li>void onCancelled(boolean cancel),在主线程中执行,如果被调用,那么<code>onPostExecute</code>方法不会被执行,详见源码解析.</li>
</ul>
<h3 id="AsyncTask的基本使用"><a href="#AsyncTask的基本使用" class="headerlink" title="AsyncTask的基本使用"></a>AsyncTask的基本使用</h3><p>一个简单的下载的例子</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AsyncTaskTest</span> <span class="keyword">extends</span> <span class="title">Activity</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">	<span class="keyword">private</span> TextView show;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span></span></span><br><span class="line"><span class="function">	</span>&#123;</span><br><span class="line">		<span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line">		setContentView(R.layout.main);</span><br><span class="line">		show = (TextView) findViewById(R.id.show);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 此方法为界面的按钮提供事件响应方法,</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">download</span><span class="params">(View source)</span> <span class="keyword">throws</span> MalformedURLException</span></span><br><span class="line"><span class="function">	</span>&#123;</span><br><span class="line">		DownTask task = <span class="keyword">new</span> DownTask(<span class="keyword">this</span>);</span><br><span class="line">		task.execute(<span class="keyword">new</span> URL(<span class="string">"http://www.crazyit.org/ethos.php"</span>));</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="class"><span class="keyword">class</span> <span class="title">DownTask</span> <span class="keyword">extends</span> <span class="title">AsyncTask</span>&lt;<span class="title">URL</span>, <span class="title">Integer</span>, <span class="title">String</span>&gt;</span></span><br><span class="line"><span class="class">	</span>&#123;</span><br><span class="line">		<span class="comment">// 可变长的输入参数，与AsyncTask.exucute()对应</span></span><br><span class="line">		ProgressDialog pdialog;</span><br><span class="line">		<span class="comment">// 定义记录已经读取行的数量</span></span><br><span class="line">		<span class="keyword">int</span> hasRead = <span class="number">0</span>;</span><br><span class="line">		Context mContext;</span><br><span class="line"></span><br><span class="line">		<span class="function"><span class="keyword">public</span> <span class="title">DownTask</span><span class="params">(Context ctx)</span></span></span><br><span class="line"><span class="function">		</span>&#123;</span><br><span class="line">			mContext = ctx;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="meta">@Override</span></span><br><span class="line">		<span class="function"><span class="keyword">protected</span> String <span class="title">doInBackground</span><span class="params">(URL... params)</span></span></span><br><span class="line"><span class="function">		</span>&#123;</span><br><span class="line">			StringBuilder sb = <span class="keyword">new</span> StringBuilder();</span><br><span class="line">			<span class="keyword">try</span></span><br><span class="line">			&#123;</span><br><span class="line">				URLConnection conn = params[<span class="number">0</span>].openConnection();</span><br><span class="line">				<span class="comment">// 打开conn连接对应的输入流，并将它包装成BufferedReader</span></span><br><span class="line">				BufferedReader br = <span class="keyword">new</span> BufferedReader(</span><br><span class="line">					<span class="keyword">new</span> InputStreamReader(conn.getInputStream()</span><br><span class="line">					, <span class="string">"utf-8"</span>));</span><br><span class="line">				String line = <span class="keyword">null</span>;</span><br><span class="line">				<span class="keyword">while</span> ((line = br.readLine()) != <span class="keyword">null</span>)</span><br><span class="line">				&#123;</span><br><span class="line">					sb.append(line + <span class="string">"\n"</span>);</span><br><span class="line">					hasRead++;</span><br><span class="line">					publishProgress(hasRead);</span><br><span class="line">				&#125;</span><br><span class="line">				<span class="keyword">return</span> sb.toString();</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">catch</span> (Exception e)</span><br><span class="line">			&#123;</span><br><span class="line">				e.printStackTrace();</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="meta">@Override</span></span><br><span class="line">		<span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onPostExecute</span><span class="params">(String result)</span></span></span><br><span class="line"><span class="function">		</span>&#123;</span><br><span class="line">			<span class="comment">// 返回HTML页面的内容</span></span><br><span class="line">			show.setText(result);</span><br><span class="line">			pdialog.dismiss();</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="meta">@Override</span></span><br><span class="line">		<span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onPreExecute</span><span class="params">()</span></span></span><br><span class="line"><span class="function">		</span>&#123;</span><br><span class="line">			pdialog = <span class="keyword">new</span> ProgressDialog(mContext);</span><br><span class="line">			<span class="comment">// 设置对话框的标题</span></span><br><span class="line">			pdialog.setTitle(<span class="string">"任务正在执行中"</span>);</span><br><span class="line">			<span class="comment">// 设置对话框 显示的内容</span></span><br><span class="line">			pdialog.setMessage(<span class="string">"任务正在执行中，敬请等待..."</span>);</span><br><span class="line">			<span class="comment">// 设置对话框不能用“取消”按钮关闭</span></span><br><span class="line">			pdialog.setCancelable(<span class="keyword">false</span>);</span><br><span class="line">			<span class="comment">// 设置该进度条的最大进度值</span></span><br><span class="line">			pdialog.setMax(<span class="number">202</span>);</span><br><span class="line">			<span class="comment">// 设置对话框的进度条风格</span></span><br><span class="line">			pdialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);</span><br><span class="line">			<span class="comment">// 设置对话框的进度条是否显示进度</span></span><br><span class="line">			pdialog.setIndeterminate(<span class="keyword">false</span>);</span><br><span class="line">			pdialog.show();</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="meta">@Override</span></span><br><span class="line">		<span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onProgressUpdate</span><span class="params">(Integer... values)</span></span></span><br><span class="line"><span class="function">		</span>&#123;</span><br><span class="line">			<span class="comment">// 更新进度</span></span><br><span class="line">			show.setText(<span class="string">"已经读取了【"</span> + values[<span class="number">0</span>] + <span class="string">"】行！"</span>);</span><br><span class="line">			pdialog.setProgress(values[<span class="number">0</span>]);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>AsyncTask的启动只需要执行:task.execute(Param … params);可以想其execute()方法中传入多个Param,代表多个任务,(eg:这个例子中可以传入多个url,那么就可以进行多个的下载),可以同时进行,其内部使用了线程池原理;</p>
<h3 id="AsyncTask中的注意事项"><a href="#AsyncTask中的注意事项" class="headerlink" title="AsyncTask中的注意事项"></a>AsyncTask中的注意事项</h3><ol>
<li>AsyncTask的类必须在主线程进行加载,也就是说第一次访问AsyncTask必须在主线程,在4.1之后的版本中,被系统自动完成,在5.0的代码中,在ActivityThread.main()中,调用了AsyncTask.init()方法来实现在主线程中被加载</li>
<li>AsyncTask对象必须在主线程中创建</li>
<li>execute()方法必须在UI线程中调用</li>
<li>不要在持续中自己调用onPreExecute(),doInBackground()等方法</li>
<li>一个AsyncTask对象执行一次execute()方法,多次执行会报错;</li>
<li>AsyncTask在不同版本上的表现是不一样的,eg:1.6版本之前,串行执行任务;1.6时,采用线程池并行处理任务;3.0开始又采用一个线程来串行执行任务;</li>
</ol>
<h3 id="AsyncTask源码解析"><a href="#AsyncTask源码解析" class="headerlink" title="AsyncTask源码解析"></a>AsyncTask源码解析</h3><p>这里分析的版本是Andoid4.0/4.2,不同的版本可能稍有不同;</p>
<p>首先从AsyncTask的execute()方法分析</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> AsyncTask&lt;Params, Progress, Result&gt; <span class="title">execute</span><span class="params">(Params... params)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> executeOnExecutor(sDefaultExecutor, params);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 额,只有一句话,具体实现在下面;</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> AsyncTask&lt;Params, Progress, Result&gt; <span class="title">executeOnExecutor</span><span class="params">(Executor exec,Params... params)</span> </span>&#123;  </span><br><span class="line">    <span class="keyword">if</span> (mStatus != Status.PENDING) &#123;  </span><br><span class="line">        <span class="keyword">switch</span> (mStatus) &#123;  </span><br><span class="line">            <span class="keyword">case</span> RUNNING:  </span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(<span class="string">"Cannot execute task:"</span>  </span><br><span class="line">                        + <span class="string">" the task is already running."</span>);  </span><br><span class="line">            <span class="keyword">case</span> FINISHED:  </span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(<span class="string">"Cannot execute task:"</span>  </span><br><span class="line">                        + <span class="string">" the task has already been executed "</span>  </span><br><span class="line">                        + <span class="string">"(a task can be executed only once)"</span>);  </span><br><span class="line">        &#125;  </span><br><span class="line">    &#125;  </span><br><span class="line">    mStatus = Status.RUNNING;  </span><br><span class="line">    onPreExecute();  </span><br><span class="line">    mWorker.mParams = params;  </span><br><span class="line">    exec.execute(mFuture);  </span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>;  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里关注的点是:</p>
<ol>
<li>最后将<code>mStatus</code>设置为<code>RUNNING</code>,由此也可以得出AsyncTask只能执行一次,否则其execute()方法一进去判断状态,如果已经是<code>RUNNING</code>,直接报错</li>
<li>执行了<code>onPreExecute()</code>方法,因此证明了onPreExecute()方法会第一个得到执行,当前依然在UI线程，所以我们可以在其中做一些准备工作。</li>
<li>调用了Executor的execute()方法，并将前面初始化的mFuture对象传了进去</li>
<li>mWorker.mParams = params,将我们传入的参数赋值给了mWorker.mParams</li>
<li>exec.execute(mFuture)</li>
</ol>
<p>相信大家对与<code>mWorker</code>和<code>mFuture</code>感到困惑,我们找到这两个类</p>
<p>其在<code>AsyncTask</code>中的定义<code>private final WorkerRunnable&lt;Params, Result&gt; mWorker;</code></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">WorkerRunnable</span>&lt;<span class="title">Params</span>, <span class="title">Result</span>&gt; <span class="keyword">implements</span> <span class="title">Callable</span>&lt;<span class="title">Result</span>&gt; </span>&#123;  </span><br><span class="line">        Params[] mParams;  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以看到是Callable的子类，且包含一个<code>mParams</code>用于保存我们传入的参数，下面看初始化mWorker的代码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">AsyncTask</span><span class="params">()</span> </span>&#123;  </span><br><span class="line">        mWorker = <span class="keyword">new</span> WorkerRunnable&lt;Params, Result&gt;() &#123;  </span><br><span class="line">          	<span class="comment">//重写了call方法,只要mWorker一启动,就执行call方法,在call方法中先执行doInBackground方法</span></span><br><span class="line">          	<span class="comment">//然后将其结果作为postResult的参数传入postResult()中,作为callable接口的返回值,此时依然在子线程中</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> Result <span class="title">call</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;  </span><br><span class="line">                mTaskInvoked.set(<span class="keyword">true</span>);  </span><br><span class="line">  				<span class="comment">//设置线程优先级:后台线程;</span></span><br><span class="line">                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  </span><br><span class="line">                <span class="comment">//noinspection unchecked </span></span><br><span class="line">                <span class="keyword">return</span> postResult(doInBackground(mParams));  </span><br><span class="line">            &#125;  </span><br><span class="line">        &#125;;  </span><br><span class="line"><span class="comment">//….  </span></span><br><span class="line">          </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以看到mWorker在构造方法中完成了初始化，并且因为是一个抽象类，在这里new了一个实现类，实现了call方法，call方法中设置mTaskInvoked=true，且最终调用doInBackground(mParams)方法，并返回Result值作为参数给postResult方法.这基本上就将 中几个重要函数的执行流程描述清楚了.</p>
<p>这里也出现了最重要的方法<code>doInBackground()</code>,我们知道<code>doInBackground()</code>的返回值是是作为参数传给<code>onPostExecute(Result res)</code>,为什么是作为参数传给了<code>postResult</code>呢?</p>
<p>其实可以想到:<code>doInBackground()</code>方法的执行是在子线程的,而<code>onPostExecute(Result res)</code>方法是在主线程的,这里直接传递是不可能的,需要一个中间的桥梁来实现线程的切换,这个桥梁就是<code>postResult();</code></p>
<p>接着往下看,<code>postResult</code>中具体做了什么</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> Result <span class="title">postResult</span><span class="params">(Result result)</span> </span>&#123;  </span><br><span class="line">        <span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>)  </span><br><span class="line">        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,  </span><br><span class="line">                <span class="keyword">new</span> AsyncTaskResult&lt;Result&gt;(<span class="keyword">this</span>, result));  <span class="comment">//第一个参数是当前的task,这个方法是task中的方法</span></span><br><span class="line">        message.sendToTarget();  </span><br><span class="line">        <span class="keyword">return</span> result;  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>果然,这里用到了<code>handler</code>的消息传递机制,是在子线程中将结果传递出去,结合之前所说的AsyncTask类的加载必须在主线程,那么我们还可以料想到这个主线程中的AsyncTask在初始化时也创建了一个handler,并且重写了handleMessage()方法,在该方法中根据Message.What,来将返回的消息结果传给onPostExecute(Result res),这样整个消息就从子线程传递到主线程了.</p>
<p>是不是上面预想的那样,接着看代码,先看一下<code>AsyncTaskResult</code>这个类里有什么,这是一个静态内部类,封装了当前的AsyncTask对象和要返回的结果集,其实这个结果集并没有什么用,因为最终取的还是第一个值 data[0].</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">AsyncTaskResult</span>&lt;<span class="title">Data</span>&gt; </span>&#123;  </span><br><span class="line">       <span class="keyword">final</span> AsyncTask mTask;  </span><br><span class="line">       <span class="keyword">final</span> Data[] mData;  </span><br><span class="line">  </span><br><span class="line">       AsyncTaskResult(AsyncTask task, Data... data) &#123;  </span><br><span class="line">           mTask = task;  </span><br><span class="line">           mData = data;  </span><br><span class="line">       &#125;  </span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>

<p>接下来看一下AsyncTask中的handler对象在哪里?</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> InternalHandler sHandler = <span class="keyword">new</span> InternalHandler(); <span class="comment">//AsyncTask中的成员变量</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//接下来看一下这个handler是专门处理详细的, 在handleMessage(msg)方法中已经说得很清楚了.</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">InternalHandler</span> <span class="keyword">extends</span> <span class="title">Handler</span> </span>&#123;  </span><br><span class="line">        <span class="meta">@SuppressWarnings</span>(&#123;<span class="string">"unchecked"</span>, <span class="string">"RawUseOfParameterizedType"</span>&#125;)  </span><br><span class="line">        <span class="meta">@Override</span>  </span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleMessage</span><span class="params">(Message msg)</span> </span>&#123;  </span><br><span class="line">            AsyncTaskResult result = (AsyncTaskResult) msg.obj;  </span><br><span class="line">            <span class="keyword">switch</span> (msg.what) &#123;  </span><br><span class="line">                <span class="keyword">case</span> MESSAGE_POST_RESULT:  </span><br><span class="line">                    <span class="comment">// There is only one result  </span></span><br><span class="line">                    result.mTask.finish(result.mData[<span class="number">0</span>]);  </span><br><span class="line">                    <span class="keyword">break</span>;  </span><br><span class="line">                <span class="keyword">case</span> MESSAGE_POST_PROGRESS:  </span><br><span class="line">                    result.mTask.onProgressUpdate(result.mData);  </span><br><span class="line">                    <span class="keyword">break</span>;  </span><br><span class="line">            &#125;  </span><br><span class="line">        &#125;  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>我们先看正常发出的标签 msg.what是<code>MESSAGE_POST_RESULT</code>,这里调用了<code>result.mTask.finish(result.mData[0]);</code></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">finish</span><span class="params">(Result result)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (isCancelled()) &#123;</span><br><span class="line">            onCancelled(result);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            onPostExecute(result);</span><br><span class="line">        &#125;</span><br><span class="line">        mStatus = Status.FINISHED;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>看到这里进行了判断,如果该任务被取消了,那么走<code>onCancelled(result)</code>的分支,否则,执行<code>onPostExecute(result)</code>,最后将状态设置为<code>最后将状态置为FINISHED</code>,整个任务就结束了.</p>
<p>mWoker看完了，应该到我们的mFuture了，我们在使用Future的时候,是将其作为参数传入Thread中的,依然实在构造方法中完成mFuture的初始化，将mWorker作为参数，复写了其done方法。done()方法是在其成员变量Callable中的call()方法执行结束之后才执行的回调,此时调用其get()方法不会阻塞主线程,</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">AsyncTask</span><span class="params">()</span> </span>&#123;  </span><br><span class="line">    ...  </span><br><span class="line">        mFuture = <span class="keyword">new</span> FutureTask&lt;Result&gt;(mWorker) &#123;  </span><br><span class="line">            <span class="meta">@Override</span>  </span><br><span class="line">            <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">done</span><span class="params">()</span> </span>&#123;  </span><br><span class="line">                <span class="keyword">try</span> &#123;  </span><br><span class="line">                    postResultIfNotInvoked(get());  </span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;  </span><br><span class="line">                    android.util.Log.w(LOG_TAG, e);  </span><br><span class="line">                &#125; <span class="keyword">catch</span> (ExecutionException e) &#123;  </span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">"An error occured while executing doInBackground()"</span>,  </span><br><span class="line">                            e.getCause());  </span><br><span class="line">                &#125; <span class="keyword">catch</span> (CancellationException e) &#123;  </span><br><span class="line">                    postResultIfNotInvoked(<span class="keyword">null</span>);  </span><br><span class="line">                &#125;  </span><br><span class="line">            &#125;  </span><br><span class="line">        &#125;;  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里在任务执行结束后调用了<code>postResultIfNotInvoked(get())</code>,看一下这个方法是如何实现的</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">postResultIfNotInvoked</span><span class="params">(Result result)</span> </span>&#123;  </span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">boolean</span> wasTaskInvoked = mTaskInvoked.get();  </span><br><span class="line">    <span class="keyword">if</span> (!wasTaskInvoked) &#123;  </span><br><span class="line">        postResult(result);  </span><br><span class="line">   &#125;  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>如果<code>mTaskInvoked</code>不为<code>true</code>，则执行<code>postResult</code>；但是在<code>mWorker</code>初始化时就已经将<code>mTaskInvoked</code>为<code>true</code>，所以一般这个<code>postResult()</code>执行不到。</p>
<p>小总结一下:这里是介绍了两个变量的初始化:分别是<code>mWorker</code> 以及<code>mFuture</code>,这里<code>mWorker</code> 其实是一个<code>Callable</code>,<code>mFuture</code>其实是一个<code>FutureTask</code></p>
<p>具体使用还请参考《疯狂Java讲义》中的关于Callable和Future的使用吧</p>
<p>好了，到了这里，已经介绍完了execute方法中出现了mWorker和mFurture，不过这里一直是初始化这两个对象的代码，并没有真正的执行。下面我们看真正调用执行的地方。</p>
<p>excute()方法</p>
<p>还记得上面的<code>execute</code>中：exec.execute(mFuture),其中<code>exec</code>为<code>executeOnExecutor(sDefaultExecutor, params)</code>中的<code>sDefaultExecutor</code><br>下面看这个<code>sDefaultExecutor</code></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">volatile</span> Executor sDefaultExecutor = SERIAL_EXECUTOR;  </span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> Executor SERIAL_EXECUTOR = <span class="keyword">new</span> SerialExecutor();  </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">SerialExecutor</span> <span class="keyword">implements</span> <span class="title">Executor</span> </span>&#123;  </span><br><span class="line">  <span class="comment">//维持一个队列,队列里面盛放的是 Runnable,而FutureTask 正好实现了 Runnable 接口;</span></span><br><span class="line">   <span class="keyword">final</span> ArrayDeque&lt;Runnable&gt; mTasks = <span class="keyword">new</span> ArrayDeque&lt;Runnable&gt;();  </span><br><span class="line">  </span><br><span class="line">   Runnable mActive;  </span><br><span class="line">  </span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">(<span class="keyword">final</span> Runnable r)</span> </span>&#123;  </span><br><span class="line">     <span class="comment">//差点又被误导了,这里是指创建了一个Runnable,其中的run方法是在线程池被调用的时候执行的,而不是现在;</span></span><br><span class="line">     <span class="comment">//这里只是往上面的队列mTask中提交一个runnable对象,这个runnable对象时什么呢,就是我们所提供的mFuture对象;</span></span><br><span class="line">            mTasks.offer(<span class="keyword">new</span> Runnable() &#123;  </span><br><span class="line">              	<span class="meta">@override</span></span><br><span class="line">                <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;  </span><br><span class="line">                    <span class="keyword">try</span> &#123;  </span><br><span class="line">                        r.run();  <span class="comment">//这个步骤本身就是一个阻塞的,会经过一段时间,执行过程中可能有新的任务添加进来</span></span><br><span class="line">                    &#125; <span class="keyword">finally</span> &#123;  </span><br><span class="line">                        scheduleNext();  </span><br><span class="line">                    &#125;  </span><br><span class="line">                &#125;  </span><br><span class="line">            &#125;);  </span><br><span class="line">            <span class="keyword">if</span> (mActive == <span class="keyword">null</span>) &#123;  <span class="comment">//当前没有要执行的任务,那么就取出队列的第一个任务开始执行;</span></span><br><span class="line">                scheduleNext();  </span><br><span class="line">            &#125;  </span><br><span class="line">        &#125;  </span><br><span class="line">   </span><br><span class="line">  <span class="function"><span class="keyword">protected</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">scheduleNext</span><span class="params">()</span> </span>&#123;  </span><br><span class="line">            <span class="keyword">if</span> ((mActive = mTasks.poll()) != <span class="keyword">null</span>) &#123;  </span><br><span class="line">                THREAD_POOL_EXECUTOR.execute(mActive);  <span class="comment">//此时才会执行其r.run()方法,真正执行的线程池是这个,而上一个SERIAL_EXECUTOR的线程池只负责线程串行的调度;</span></span><br><span class="line">            &#125;  </span><br><span class="line">        &#125;  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以看到sDefaultExecutor其实为SerialExecutor的一个实例,(SerialExecutor实现了Executor接口,该接口中只有一个void execute(Runnable r)方法)其内部维持一个任务队列;直接看其execute（Runnable runnable）方法，将runnable放入mTasks队尾,但是这里提供的Runnable并不是AsyncTask在构造方法中创建的FutureTask(虽然FutureTask也是一个Runnable),而是自己new了一个Runnable对象,在其run的内部手动调用了FutureTask.run(),最后执行scheduleNext()方法.</p>
<p>这个时候就要说一下其实AsyncTask其实是串行执行任务的,我们向<code>ArrayDeque&lt;Runnable&gt; mTasks</code>中添加一个任务(注意这里只是添加并不是执行…),添加完后都会判断mActivie是不是为null,如果此时没有任务在执行,那么就会调用scheduleNext()方法,但是此时不为null的话,就什么也不做,仅仅是添加任务,而当scheduleNext()中执行完一个任务后,其finally中会再次调用scheduleNext()方法,执行下一个任务,这就做到了串行…</p>
<h2 id="问题-既然AsyncTask只能execute一次-要线程池干什么"><a href="#问题-既然AsyncTask只能execute一次-要线程池干什么" class="headerlink" title="问题:既然AsyncTask只能execute一次,要线程池干什么?"></a>问题:既然AsyncTask只能execute一次,要线程池干什么?</h2><p>两个原因:</p>
<p>(一)同一个AsyncTask只能execute()一次,如果代码是</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> AsyncTask().execute();</span><br><span class="line"><span class="keyword">new</span> AsyncTask().execute();</span><br><span class="line"><span class="keyword">new</span> AsyncTask().execute();</span><br><span class="line"><span class="keyword">new</span> AsyncTask().execute();</span><br><span class="line"><span class="keyword">new</span> AsyncTask().execute();</span><br></pre></td></tr></table></figure>

<p>那么就会有多个任务了,注意:AsyncTask中的线程池是是<code>static final</code>的,这样就会产生多任务了,那么线程池自然也就派上用场了</p>
<p>(二)在3.0之前的版本,如果提交了多个任务,那么其线程池不是串行执行的,而是并行执行的.</p>
<h2 id="问题-在3-0之后可以依旧使用任务并发执行吗"><a href="#问题-在3-0之后可以依旧使用任务并发执行吗" class="headerlink" title="问题:在3.0之后可以依旧使用任务并发执行吗?"></a>问题:在3.0之后可以依旧使用任务并发执行吗?</h2><p>可以,只不过不要调用<code>new AsyncTask().execute();</code>而是调用<code>new AsyncTask().executeOnExecutor(Executor exec,Params... params)</code>,这时候,我们就可以不使用默认的sDefaultExecutor了,而是我们自己提供一个线程池,实现并发.</p>
<p>我们知道在execute()方法中其实是调用了executeOnExecutor(),而这个方法在3.0之前是没有的,3.0之前直接把execute(),然后线程池就并发执行了,当然了,线程池你也知道,如果你提交的任务数过多,就直接报错的,这也是3.0之前的一个缺点,不可配置.</p>




    </div>










    <footer class="post-footer">








        <div class="post-eof"></div>

    </footer>
  </div>



  </article>











  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">



  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://zachaxy.github.io/2017/06/05/Objective-C快速上手/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="zachaxy">
      <meta itemprop="description" content>
      <meta itemprop="image" content="/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="zachaxy">
    </span>


      <header class="post-header">



          <h2 class="post-title" itemprop="name headline">

                <a class="post-title-link" href="/2017/06/05/Objective-C快速上手/" itemprop="url">Objective-C快速上手</a></h2>


        <div class="post-meta">
          <span class="post-time">

              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>

                <span class="post-meta-item-text">发表于</span>

              <time title="创建于" itemprop="dateCreated datePublished" datetime="2017-06-05T15:28:42+08:00">
                2017-06-05
              </time>





          </span>













            <div class="post-wordcount">


                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>

                  <span class="post-meta-item-text">字数统计&#58;</span>

                <span title="字数统计">
                  3,086
                </span>





            </div>




        </div>
      </header>





    <div class="post-body" itemprop="articleBody">







            <blockquote>
<p>本文是 Objective-C 的快速上手文章，只要你之前有 Java 或者 C++ 的基础，相信入门 Objective-C ，只需要很短的时间。</p>
</blockquote>
<h1 id="OC-语法概览"><a href="#OC-语法概览" class="headerlink" title="OC 语法概览"></a>OC 语法概览</h1><h2 id="源代码文件拓展名对比"><a href="#源代码文件拓展名对比" class="headerlink" title="源代码文件拓展名对比"></a>源代码文件拓展名对比</h2><table>
<thead>
<tr>
<th></th>
<th>头文件</th>
<th>实现文件</th>
</tr>
</thead>
<tbody><tr>
<td>c</td>
<td>.h</td>
<td>.c</td>
</tr>
<tr>
<td>c++</td>
<td>.h</td>
<td>.cpp</td>
</tr>
<tr>
<td>oc</td>
<td>.h</td>
<td>.m</td>
</tr>
<tr>
<td>oc&amp;&amp;c++</td>
<td>.h</td>
<td>.mm</td>
</tr>
</tbody></table>
<h2 id="类的定义"><a href="#类的定义" class="headerlink" title="类的定义"></a>类的定义</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">@interface MyClass:NSObject</span><br><span class="line"></span><br><span class="line">@end</span><br></pre></td></tr></table></figure>

<p>定义一个类，以@interface MyClass:NSObject 开头，以@end 结尾，类名就是 MyClass，后面跟一个冒号，其含义就是 Java 中的 extends 关键字， 表示继承自 NSObject 类</p>
<p>遇到@符号，编译器会进行相应的预处理。</p>
<p>声明之中只有属性的定义，方法的声明，并没有方法的具体实现，具体实现是在类的实现中。从这个角度看，类的声明又很像是 Java 中的接口。类的声明一般是放在头文件中<code>.h</code>中</p>
<h2 id="属性的定义"><a href="#属性的定义" class="headerlink" title="属性的定义"></a>属性的定义</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">@interface Person:NSObject</span><br><span class="line">	@property NSString *firstName;</span><br><span class="line">	@property NSNumber *age;</span><br><span class="line">	@property int id;</span><br><span class="line">	@property (readonly)NSString * lastName;</span><br><span class="line">@end</span><br></pre></td></tr></table></figure>

<p>定义属性必须以@property 开头，然后是属性的类型，后面是变量名，如果变量是以<code>*</code>开头的，那么表明这个变量是一个指针，指向的是一块对内存中的区域；</p>
<p>同时，属性也有基本类型，eg：int，double 等，这表明的是一个基本类型；</p>
<p>用(readonly)前缀的变量，表明该变量时只读的，这和 Java 中的 final 关键字很类似</p>
<h2 id="方法的定义"><a href="#方法的定义" class="headerlink" title="方法的定义"></a>方法的定义</h2><p>减号方法（普通方法，又称为对象方法）</p>
<p>加号方法（类方法，又称为静态方法）</p>
<h2 id="类的实现"><a href="#类的实现" class="headerlink" title="类的实现"></a>类的实现</h2><p>类的声明和实现是分开的，也就是说声明之中只有属性的定义，方法的声明，并没有方法的具体实现，具体实现是在类的实现中。从这个角度看，类的声明又很像是 Java 中的接口</p>
<h2 id="完整的例子"><a href="#完整的例子" class="headerlink" title="完整的例子"></a>完整的例子</h2><p>定义类 <code>Person.h</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">@interface Person:NSObject</span><br><span class="line">-(void)sayHello;</span><br><span class="line">@end</span><br></pre></td></tr></table></figure>

<p>实现类<code>Person.m</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"># import &quot;Person.h&quot;</span><br><span class="line"></span><br><span class="line">@implementation Person</span><br><span class="line">-(void)sayHello&#123;</span><br><span class="line"> 	NSLog(@&quot;hello,world&quot;);</span><br><span class="line">&#125; </span><br><span class="line">@end</span><br></pre></td></tr></table></figure>

<p>在实现方法中，有一个 NSLog 的方法，这个方法是一个静态方法，类似于 Java 中的 System.out.println 方法；</p>
<p>在”hello world”字符串之前，加一个@符号，表明这是一个 OC 类型的字符串，OC 是完美支持 C 语言的，如果不加@符号，那么表明这是一个 C 语言类型的字符串（其实就是一个字符数组）。</p>
<h2 id="简单的-main-方法"><a href="#简单的-main-方法" class="headerlink" title="简单的 main 方法"></a>简单的 main 方法</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">#import &lt;Foundation/Foundation.h&gt;</span><br><span class="line"></span><br><span class="line">int main(int argc, char const *argv[])</span><br><span class="line">&#123;</span><br><span class="line">	/* code */</span><br><span class="line">	@autoreleasepool&#123;</span><br><span class="line">		NSLog(@&quot;hello world&quot;);</span><br><span class="line">	&#125;</span><br><span class="line">	return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="变量"><a href="#变量" class="headerlink" title="变量"></a>变量</h1><h2 id="基本数据类型"><a href="#基本数据类型" class="headerlink" title="基本数据类型"></a>基本数据类型</h2><ul>
<li>int：4byte</li>
<li>float：4byte</li>
<li>double：8byte</li>
<li>char；1byte</li>
<li>boolean ：和其它语言不一样的是，oc 中 true 是用 1 来表示的，false 是用</li>
</ul>
<p>限定词：</p>
<ul>
<li>long：long int a，表示加长版的 int，简写为：long a</li>
<li>long long：long long int a，表示加长版的 long，简写为：long long  a</li>
<li>short：short int a，表示简短版的 short，简写为： short a</li>
<li>unsigned：unsigned int a，表示无符号整形</li>
<li>signed：signed int a；表示有符号的整形</li>
</ul>
<p>字符串:并不是基础类型，但是可以平时使用很多</p>
<p>OC 字符串类型：NSString:@”hello”，在打印时使用%@</p>
<p>C 语言字符串类型：”hello”，在打印时，使用%s</p>
<h1 id="条件控制"><a href="#条件控制" class="headerlink" title="条件控制"></a>条件控制</h1><h2 id="if"><a href="#if" class="headerlink" title="if"></a>if</h2><p>关于 OC 中的布尔值，和其它语言不同，并不是 true、false，而是用 0 来表示 false，如果 if 中的条件表达式中的值不是 0，其它所有的数字，或者字符串都可以表示 true；</p>
<h2 id="goto"><a href="#goto" class="headerlink" title="goto"></a>goto</h2><p>和 C 语言的 goto 语法相同，定义一个标签，只要不是关键字都可以，后面的代码用花括号包起来，不包也可以；然后在想要跳转的地方进行 goto label 的跳转；</p>
<p>标签并不一定要定义在 goto 语句之前出现，后面也是可以的；</p>
<h1 id="循环语句"><a href="#循环语句" class="headerlink" title="循环语句"></a>循环语句</h1><p>for、while 和 C 语言完全一样</p>
<h1 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h1><p>定义：同 C 语言</p>
<p>调用：同 C 语言</p>
<h1 id="Objective-C-面向对象"><a href="#Objective-C-面向对象" class="headerlink" title="Objective-C 面向对象"></a>Objective-C 面向对象</h1><p>OOP：面向对象编程</p>
<p>OOA：面向对象分析</p>
<p>OOD：面向对象设计</p>
<h2 id="创建类和对象"><a href="#创建类和对象" class="headerlink" title="创建类和对象"></a>创建类和对象</h2><p>在 Xcode 中创建一个类，右键 new File，选择 iOS 中的 Source，选择 Cocoa Touch Class,选择继承自 NSObject，这样的话就会生成两个文件，分别是一个 .h  和 .m 文件，h 文件中写类的声明，m 中写类的实现。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">// Person.h</span><br><span class="line"># import&lt;Foundation/Foundation.h&gt;</span><br><span class="line"></span><br><span class="line">@interface Person : NSObject&#123;</span><br><span class="line">	NSString *name</span><br><span class="line">	int age;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">//定义一个属性，这个属性就相当于对成员变量的 get/set</span><br><span class="line">@property(nonatomic,strong)NSString *personName;</span><br><span class="line"></span><br><span class="line">//上面属性的定义的本质就是定义了如下方法：</span><br><span class="line">/*- (void) setName:(NSString *)name;</span><br><span class="line">- (NSString *)getName;*/</span><br><span class="line">@end</span><br></pre></td></tr></table></figure>

<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">// Person.m</span><br><span class="line">#import &quot;Person.h&quot;</span><br><span class="line"></span><br><span class="line">@implementation Person</span><br><span class="line"></span><br><span class="line">@synthesize personName = name;</span><br><span class="line"></span><br><span class="line">- (instancetype)init&#123;</span><br><span class="line">	self = [super init];</span><br><span class="line">	if (self)</span><br><span class="line">	&#123;</span><br><span class="line">		name = @&quot;张三&quot;；</span><br><span class="line">	&#125;</span><br><span class="line">	return self；</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">/*- (void) setName:(NSString *)name&#123;</span><br><span class="line">	personName = name;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">- (NSString *)getName&#123;</span><br><span class="line">	return personName;</span><br><span class="line">&#125;*/</span><br><span class="line"></span><br><span class="line">@end</span><br></pre></td></tr></table></figure>

<p>在 main 方法中使用该对象；</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">#import &lt;Foundation/Foundation.h&gt;</span><br><span class="line">#import &quot;Person.h&quot;</span><br><span class="line"></span><br><span class="line">int main(int argc, char const *argv[])</span><br><span class="line">&#123;</span><br><span class="line"> 	@autoreleasepool&#123;</span><br><span class="line">		Person *p1 = [[Person alloc]init]; </span><br><span class="line">        p1.personName = @&quot;张三&quot;;</span><br><span class="line">      	Person *p2 = [Person new];</span><br><span class="line"> 	&#125;</span><br><span class="line">	return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>解析<code>Person *p1 = [[Person alloc] init];</code></p>
<p>首先，Person 是类名，后面跟变量名，但前边必须有一个 <code>*</code>,表示指针，所以在 OC 中，所有的对象都是一个指针，这其实和 Java 是类似的，因为 Java 中的对象的引用，实际上就是指向对内存中的地址，其实也就是一个指针。</p>
<ul>
<li>对于静态方法：[类名 方法名]</li>
<li>对于非静态方法：[对象名 方法名]</li>
</ul>
<p>alloc 是一个方法名，用来分配内存，init 也是一个方法名，用来初始化对象；</p>
<p>另一种初始化对象的方法是 [类名 new]，直接使用一个 new 的关键字，就代表了 alloc 和 init 两个方法，直接申请内存并初始话对象，但是这种方法与整体编码风格不搭，所以还是使用先 alloc 再 init 的方法。</p>
<h2 id="成员变量和属性"><a href="#成员变量和属性" class="headerlink" title="成员变量和属性"></a>成员变量和属性</h2><p>类内使用成员变量，类外使用属性</p>
<p>因为成员变量是只能在类内使用的，属性存在的意义是让类外访问成员变量，充当成员变量的外部访问接口。从这个角度来分析，这又有点像 Java 中的 get/set 方法；</p>
<p>所以我们在 h 文件中定义了一个属性，名字叫做 personName，那么我们直接在类外对这个属性赋值或者取值就可以了，如果没有定义属性，那么我们就必须自己手写 get/set 方法，代码量增加了。然而如何将属性和成员变量进行关联呢？</p>
<p>成员变量名：name</p>
<p>属性名：personName</p>
<p>为了建立关联，我们要在 m 文件中定义关联：<code>@synthesize personName = name;</code></p>
<p>如果成员变量名和属性名一样的话，eg 都是 name，那么关联更为简单：</p>
<p><code>@synthesize name;</code></p>
<p>但是这样写的话，带来一个困扰，就是在类中调用的究竟是成员变量还是属性，这是两个对象，这给代码的编写带来迷惑，所以不建议将属性名和成员变量名写成一样的。</p>
<p>在类内是完全没有必要调用属性的，因为成员变量在类内是完全可用的，所以我们在类内应该使用成员变量，属性是给类外使用的。</p>
<p>苹果官方推荐的做法是，成员变量使用 _name，而属性使用 name，以作区分，当然这只是一个建议，并不一定必须要这样；然后在 m 文件中使用 @synthesize 关键字进行对应，这是较老的版本的做法。</p>
<p>在新版本中则不需要这么做，如果我们想要将一个属性和成员变量进行对接，直接定义一个属性即可，也不需要再 m 文件中使用 @synthesize 关键字进行对接，SDK 已经自动帮我们生成了<code>_ 属性名</code>的成员变量，在类中我们只可以使用 _name 来获取成员变量。</p>
<p>因为成员变量是在类内才能进行调用的，所以现在成员变量在 m 文件中定义和在 h 文件中定义是没有区别的。建议在 m 文件定义吧，但是属性是一定要在 h 文件中定义的。</p>
<h2 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h2><h3 id="方法的声明"><a href="#方法的声明" class="headerlink" title="方法的声明"></a>方法的声明</h3><p>方法的定义是在 h 文件中，这里只有一个方法的声明，声明的形式：</p>
<p>方法类型 (方法返回值类型) 方法名 : (参数 1 类型) 参数 1 名字  </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-(returnType)methodName:(typeName) variable1 :(typeName)variable2;</span><br></pre></td></tr></table></figure>

<ul>
<li>方法类型，可选的有 + 或者 -，其中前者代表类方法，后者代表对象方法</li>
<li>方法返回值类型：要用括号包裹起来，eg：(void) ，(int) ，(NSString *)等</li>
<li>方法名：和其它语言相同</li>
<li>方法参数：没有参数，那么方法名后面就不用跟冒号了，如果有参数，那么方法名后边要跟一个冒号，然后</li>
</ul>
<p>eg：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">- void showName;</span><br><span class="line">- void showName : (NSString *) name;</span><br><span class="line">- void showName : (NSString *) name andAge:(int) age;</span><br></pre></td></tr></table></figure>

<h3 id="方法的实现"><a href="#方法的实现" class="headerlink" title="方法的实现"></a>方法的实现</h3><p>方法的实现是在 m 文件中；</p>
<h3 id="方法的调用"><a href="#方法的调用" class="headerlink" title="方法的调用"></a>方法的调用</h3><p>使用[ ] ，完成调用，分为两种情况：</p>
<ul>
<li>对于静态方法：[类名 方法名]</li>
<li>对于非静态方法：[对象名 方法名]</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Person p1 = [[Person alloc] init];</span><br><span class="line">[p1 showName]；</span><br><span class="line">[p1 showName: @&quot;zx&quot;]；</span><br><span class="line">[p1 showName: @&quot;zx&quot; andAge: 24]；</span><br></pre></td></tr></table></figure>

<h3 id="关于-init-方法"><a href="#关于-init-方法" class="headerlink" title="关于 init 方法"></a>关于 init 方法</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"># import&lt;Foundation/Foundation.h&gt;</span><br><span class="line"></span><br><span class="line">@interface Person : NSObject&#123;</span><br><span class="line"> </span><br><span class="line">&#125;</span><br><span class="line">- (id) init;</span><br><span class="line">- (instancetype)init;</span><br><span class="line">- (instancetype)initPerson:(NSString *)name :(int)age;</span><br><span class="line">@end</span><br></pre></td></tr></table></figure>

<p>关于 init 方法，我们可以在类的声明中重写，其返回值类型可以是 id，表示任何类型；返回值类型可以使 instancetype，表示当前类型；单纯的初始化方法的话，其实都可以，但是要赋值的时候可能会出错，所以推荐使用后者；因为我们所有的类其实都是 NSObject 的子类，所以 init 方法不在类声明中也是可以的，如果声明的话，就相当于重写了；这里重写 init 方法的意义在于给初始化方法提供多个参数，供具体的类来使用。例如上面的 initPerson，具体实现略；</p>
<p>那么在使用时就可以用下面的方法来调用：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line">Person * p1 = [[Person alloc]initPerson:@&quot;zx&quot;:20];</span><br><span class="line">...</span><br></pre></td></tr></table></figure>

<h2 id="封装"><a href="#封装" class="headerlink" title="封装"></a>封装</h2><p>屏蔽内部实现细节，只提供一个使用的接口。和 Java 无异；</p>
<h3 id="访问修饰符"><a href="#访问修饰符" class="headerlink" title="访问修饰符"></a>访问修饰符</h3><p>可用来修饰成员变量（注意不是属性）</p>
<ul>
<li>@public :在类内，类外都可以使用，并可以被继承；在类外的使用方法是：p1-&gt;name</li>
<li>@protected：默认访问修饰符，在类内可以使用，类外不可被使用，可以被继承</li>
<li>@private：只能在类内使用，不可以被继承；</li>
<li>@package：框架权限，在框架内相当于 protected，在框架外，相当于 private；</li>
</ul>
<p><strong>注：方法是没有访问修饰符</strong></p>
<p>这个用法完全和 C 语言是一样的，如果不想让人在外面使用，可以不在 h 文件中申明该方法，那么在类外就无法调用该方法了（类外引用的永远都是 h 文件，而看不到具体实现），调用直接报错；而我们可以在 m 文件中写一个方法的声明，这样的话就可以在 m 文件内部被其它方法调用了。</p>
<h2 id="继承"><a href="#继承" class="headerlink" title="继承"></a>继承</h2><p>OC 中也是单继承的，要想实现多继承的效果，可以使用协议；</p>
<p>关于继承，我们在之前其实已经见到过了，就是继承了 NSObject；NSObject 是所有类型的基类；</p>
<p>继承的语法：</p>
<ol>
<li><p>在 h 文件中</p>
<p>这时 import 的是父类的 h 文件，并且在 @interface 后面类名，后面加冒号，然后跟父类名字；</p>
</li>
<li><p>在 m 文件中，我们可以对父类中在 h 文件中声明的方法进行一个覆盖，不覆盖默认使用的是父类中的方法</p>
</li>
</ol>
<p>父类在 h 文件中声明的方法才可以被子类继承，如果没有在 h 文件中声明，那么表示只有父类自己内部可以用，外部，子类均不可用。</p>
<h2 id="多态"><a href="#多态" class="headerlink" title="多态"></a>多态</h2><p>父类引用指向子类对象；</p>
<p>OC 中不支持方法重载，只能重写</p>
<p>其它特点和 Java 无异。</p>




    </div>










    <footer class="post-footer">








        <div class="post-eof"></div>

    </footer>
  </div>



  </article>











  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">



  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://zachaxy.github.io/2017/05/28/Volley源码解析-线程切换/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="zachaxy">
      <meta itemprop="description" content>
      <meta itemprop="image" content="/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="zachaxy">
    </span>


      <header class="post-header">



          <h2 class="post-title" itemprop="name headline">

                <a class="post-title-link" href="/2017/05/28/Volley源码解析-线程切换/" itemprop="url">Volley源码解析-线程切换</a></h2>


        <div class="post-meta">
          <span class="post-time">

              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>

                <span class="post-meta-item-text">发表于</span>

              <time title="创建于" itemprop="dateCreated datePublished" datetime="2017-05-28T17:52:23+08:00">
                2017-05-28
              </time>





          </span>













            <div class="post-wordcount">


                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>

                  <span class="post-meta-item-text">字数统计&#58;</span>

                <span title="字数统计">
                  1,166
                </span>





            </div>




        </div>
      </header>





    <div class="post-body" itemprop="articleBody">







            <h1 id="ResponseDelivery-接口"><a href="#ResponseDelivery-接口" class="headerlink" title="ResponseDelivery 接口"></a>ResponseDelivery 接口</h1><p>无论是执行网络请求还是从本地文件中读取缓存，都是放在子线程中去执行的，那么读取到响应之后，要做的工作就是把响应从子线程分发到 UI 线程，本节将学习 Volley 中是如何实现这一功能的。</p>
<p>这里用到了一个接口：<strong>ResponseDelivery</strong>，其中定义了一些方法，用来将响应从子线程发送的 UI 线程。</p>
<ul>
<li><code>public void postResponse(Request&lt;?&gt; request, Response&lt;?&gt; response)</code>：将 response 发送到 UI 线程；</li>
<li><code>public void postResponse(Request&lt;?&gt; request, Response&lt;?&gt; response, Runnable runnable)</code>：和上一个方法功能类似，只不过多加了一个 Runnable，其作用是在 response 被发送到 UI 线程之后，执行该 runnable</li>
<li><code>public void postError(Request&lt;?&gt; request, VolleyError error)</code>：在网络请求过程中出现了错误，Volley 会将该错误封装成一个 VolleyError，调用该方法可以将该 VolleyError 发送的 UI 线程，由具体的业务处理不同的错误，显示不同的提示信息；</li>
</ul>
<h1 id="ExecutorDelivery-实现类"><a href="#ExecutorDelivery-实现类" class="headerlink" title="ExecutorDelivery 实现类"></a>ExecutorDelivery 实现类</h1><h2 id="构造方法"><a href="#构造方法" class="headerlink" title="构造方法"></a>构造方法</h2><p>而 ResponseDelivery 只有一个实现类——ExecutorDelivery，无论是在 NetworkDispatcher 中还是 CacheDispatcher 中得到了 response，都会调用该类的 postResponse 方法，将结果发送到 UI 线程，ExecutorDelivery 是何时被创建的呢？答案就是创建 RequestQueue 时，在 RequestQueue 的构造方法中。ExecutorDelivery 的创建方式是：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> ExecutorDelivery(<span class="keyword">new</span> Handler(Looper.getMainLooper()));</span><br></pre></td></tr></table></figure>

<p>这里看到构造方法中传入了一个 Handler，并且传入的是 MainLooper，那么首先子线程向 UI 线程发送数据的机制肯定是通过 Handler 无疑了。</p>
<h2 id="重要成员变量"><a href="#重要成员变量" class="headerlink" title="重要成员变量"></a>重要成员变量</h2><p>ExecutorDelivery 中还有一个很重要的成员变量<code>Executor mResponsePoster</code>这是一个线程池，其创建过程也是在构造方法中：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ExecutorDelivery</span><span class="params">(<span class="keyword">final</span> Handler handler)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// Make an Executor that just wraps the handler.</span></span><br><span class="line">    mResponsePoster = <span class="keyword">new</span> Executor() &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">(Runnable command)</span> </span>&#123;</span><br><span class="line">            handler.post(command);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以看出，ExecutorDelivery 中线程池 mResponsePoster 的作用就是在线程中调用 handler，发送 runnable，通过这种形式使得 runnable 在 UI 线程中执行。</p>
<h2 id="内部类——Runnable-子类"><a href="#内部类——Runnable-子类" class="headerlink" title="内部类——Runnable 子类"></a>内部类——Runnable 子类</h2><p>上面提到线程池的内部是通过 handler，发送 runnable，通过这种形式使得 runnable 在 UI 线程中执行，那么这个 runnable 对象又是如何与 request 和 response 关联的呢？这就用到了 ExecutorDelivery 中的一个内部类 ResponseDeliveryRunnable</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">ResponseDeliveryRunnable</span> <span class="keyword">implements</span> <span class="title">Runnable</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Request mRequest;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Response mResponse;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Runnable mRunnable;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ResponseDeliveryRunnable</span><span class="params">(Request request, Response response, Runnable runnable)</span> </span>&#123;</span><br><span class="line">        mRequest = request;</span><br><span class="line">        mResponse = response;</span><br><span class="line">        mRunnable = runnable;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>)</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// If this request has canceled, finish it and don't deliver.</span></span><br><span class="line">        <span class="keyword">if</span> (mRequest.isCanceled()) &#123;</span><br><span class="line">            mRequest.finish(<span class="string">"canceled-at-delivery"</span>);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Deliver a normal response or error, depending.</span></span><br><span class="line">        <span class="keyword">if</span> (mResponse.isSuccess()) &#123;</span><br><span class="line">            mRequest.deliverResponse(mResponse.result);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            mRequest.deliverError(mResponse.error);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// If this is an intermediate response, add a marker, otherwise we're done</span></span><br><span class="line">        <span class="comment">// and the request can be finished.</span></span><br><span class="line">        <span class="keyword">if</span> (mResponse.intermediate) &#123;</span><br><span class="line">            mRequest.addMarker(<span class="string">"intermediate-response"</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            mRequest.finish(<span class="string">"done"</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// If we have been provided a post-delivery runnable, run it.</span></span><br><span class="line">        <span class="keyword">if</span> (mRunnable != <span class="keyword">null</span>) &#123;</span><br><span class="line">            mRunnable.run();</span><br><span class="line">        &#125;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>通过 ResponseDeliveryRunnable 的构造方法，将 request，response 传入，同时还可以在传入一个 Runnable 对象，这在 ResponseDelivery 的第二个接口中有提到过，该 Runnable 会在 response 被发送到 UI 线程后立即执行。</p>
<p>那么 ResponseDeliveryRunnable 类中的 run 方法则是整个线程切换的核心了；</p>
<p>要明确一点是，如果能执行到这里，那么 request 一定是得到 response 了</p>
<p>首先，判断该 request 是不是在得到请求还为发送到 UI 线程之前被取消了，如果是的话，执行 request 的 finish 方法</p>
<p>接着，如果 response 成功返回了，那么就调用 request 的 deliverResponse 方法，这个方法也很眼熟吧，这是我们在自定义 request 时必须要实现的方法，这个方法内部一般是回调 request 中的 listener 的 onResponse，否则，如果该 response 没有成功返回，会调用 request 的 deliverError 方法，而这个方法内部 request 的默认实现是调用 ErrorListener 的 onErrorResponse 方法。</p>
<p>最后调用 request 的 finish 方法，表示该请求已经执行结束了，同时，如果 ResponseDeliveryRunnable 的构造方法中的第三个参数 runnable 不为空，立即执行该 runnable 的 run 方法。</p>
<p>至此，在子线程中的 response 就被发送到 UI 线程中去了。其实说到这里，你可能发现，在 Android 体系中，很多框架都要在子线程中执行任务，然后将结果传输到 UI 线程，其处理的方式大多类似，那就是 “线程池” + “Handler” 的组合 。较为知名的框架<code>RxJava</code>，<code>EventBus</code>等都是这样实现的。关于 RxJava 的实现方式，请参考之前的一片文章<a href="https://zachaxy.github.io/2017/04/03/RxJava%E6%BA%90%E7%A0%81%E8%AF%A6%E8%A7%A3-%E7%BA%BF%E7%A8%8B%E5%88%87%E6%8D%A2%E5%8E%9F%E7%90%86/">RxJava源码详解-线程切换原理</a></p>




    </div>










    <footer class="post-footer">








        <div class="post-eof"></div>

    </footer>
  </div>



  </article>











  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">



  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://zachaxy.github.io/2017/05/24/Volley源码解析-网络/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="zachaxy">
      <meta itemprop="description" content>
      <meta itemprop="image" content="/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="zachaxy">
    </span>


      <header class="post-header">



          <h2 class="post-title" itemprop="name headline">

                <a class="post-title-link" href="/2017/05/24/Volley源码解析-网络/" itemprop="url">Volley源码解析-网络</a></h2>


        <div class="post-meta">
          <span class="post-time">

              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>

                <span class="post-meta-item-text">发表于</span>

              <time title="创建于" itemprop="dateCreated datePublished" datetime="2017-05-24T22:31:01+08:00">
                2017-05-24
              </time>





          </span>













            <div class="post-wordcount">


                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>

                  <span class="post-meta-item-text">字数统计&#58;</span>

                <span title="字数统计">
                  3,507
                </span>





            </div>




        </div>
      </header>





    <div class="post-body" itemprop="articleBody">







            <hr>
<h1 id="NetWork-接口"><a href="#NetWork-接口" class="headerlink" title="NetWork 接口"></a>NetWork 接口</h1><p>一个 request 并不是一开始就被添加进 mNetworkQueue 中的，而是先添加进 mCacheQueue，如果缓存不存在或者缓存失效，才会走网络路线，这里一旦用到了网络，就用到了 NetWork 接口，该接口是整个 Volley 库的网络请求的最外层的封装，其内部只包含了一个方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Network</span> </span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Performs the specified request.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> request Request to process</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> A &#123;<span class="doctag">@link</span> NetworkResponse&#125; with data and caching metadata; will never be null</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> VolleyError on errors</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> NetworkResponse <span class="title">performRequest</span><span class="params">(Request&lt;?&gt; request)</span> <span class="keyword">throws</span> VolleyError</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>执行网络请求，返回 NetWorkResponse，而该接口只有一个实现类——BasicNetwork，那么就看一下 BasicNetwork 中是如何实现该方法，执行网络请求的。</p>
<h2 id="BasicNetwork"><a href="#BasicNetwork" class="headerlink" title="BasicNetwork"></a>BasicNetwork</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> NetworkResponse <span class="title">performRequest</span><span class="params">(Request&lt;?&gt; request)</span> <span class="keyword">throws</span> VolleyError </span>&#123;</span><br><span class="line">    <span class="keyword">long</span> requestStart = SystemClock.elapsedRealtime();</span><br><span class="line">    <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">        HttpResponse httpResponse = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">byte</span>[] responseContents = <span class="keyword">null</span>;</span><br><span class="line">        Map&lt;String, String&gt; responseHeaders = <span class="keyword">new</span> HashMap&lt;String, String&gt;();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// Gather headers.</span></span><br><span class="line">            Map&lt;String, String&gt; headers = <span class="keyword">new</span> HashMap&lt;String, String&gt;();</span><br><span class="line">            addCacheHeaders(headers, request.getCacheEntry());</span><br><span class="line">            httpResponse = mHttpStack.performRequest(request, headers);</span><br><span class="line">            StatusLine statusLine = httpResponse.getStatusLine();</span><br><span class="line">            <span class="keyword">int</span> statusCode = statusLine.getStatusCode();</span><br><span class="line"></span><br><span class="line">            responseHeaders = convertHeaders(httpResponse.getAllHeaders());</span><br><span class="line">            <span class="comment">// Handle cache validation.</span></span><br><span class="line">            <span class="keyword">if</span> (statusCode == HttpStatus.SC_NOT_MODIFIED) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">new</span> NetworkResponse(HttpStatus.SC_NOT_MODIFIED,</span><br><span class="line">                        request.getCacheEntry().data, responseHeaders, <span class="keyword">true</span>);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// Some responses such as 204s do not have content.  We must check.</span></span><br><span class="line">            <span class="keyword">if</span> (httpResponse.getEntity() != <span class="keyword">null</span>) &#123;</span><br><span class="line">              responseContents = entityToBytes(httpResponse.getEntity());</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">              <span class="comment">// Add 0 byte response as a way of honestly representing a</span></span><br><span class="line">              <span class="comment">// no-content request.</span></span><br><span class="line">              responseContents = <span class="keyword">new</span> <span class="keyword">byte</span>[<span class="number">0</span>];</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// if the request is slow, log it.</span></span><br><span class="line">            <span class="keyword">long</span> requestLifetime = SystemClock.elapsedRealtime() - requestStart;</span><br><span class="line">            logSlowRequests(requestLifetime, request, responseContents, statusLine);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (statusCode &lt; <span class="number">200</span> || statusCode &gt; <span class="number">299</span>) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> IOException();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> NetworkResponse(statusCode, responseContents, responseHeaders, <span class="keyword">false</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (SocketTimeoutException e) &#123;</span><br><span class="line">            attemptRetryOnException(<span class="string">"socket"</span>, request, <span class="keyword">new</span> TimeoutError());</span><br><span class="line">        &#125; <span class="keyword">catch</span> (ConnectTimeoutException e) &#123;</span><br><span class="line">            attemptRetryOnException(<span class="string">"connection"</span>, request, <span class="keyword">new</span> TimeoutError());</span><br><span class="line">        &#125; <span class="keyword">catch</span> (MalformedURLException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">"Bad URL "</span> + request.getUrl(), e);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            <span class="keyword">int</span> statusCode = <span class="number">0</span>;</span><br><span class="line">            NetworkResponse networkResponse = <span class="keyword">null</span>;</span><br><span class="line">            <span class="keyword">if</span> (httpResponse != <span class="keyword">null</span>) &#123;</span><br><span class="line">                statusCode = httpResponse.getStatusLine().getStatusCode();</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> NoConnectionError(e);</span><br><span class="line">            &#125;</span><br><span class="line">            VolleyLog.e(<span class="string">"Unexpected response code %d for %s"</span>, statusCode, request.getUrl());</span><br><span class="line">            <span class="keyword">if</span> (responseContents != <span class="keyword">null</span>) &#123;</span><br><span class="line">                networkResponse = <span class="keyword">new</span> NetworkResponse(statusCode, responseContents,</span><br><span class="line">                        responseHeaders, <span class="keyword">false</span>);</span><br><span class="line">                <span class="keyword">if</span> (statusCode == HttpStatus.SC_UNAUTHORIZED ||</span><br><span class="line">                        statusCode == HttpStatus.SC_FORBIDDEN) &#123;</span><br><span class="line">                    attemptRetryOnException(<span class="string">"auth"</span>,</span><br><span class="line">                            request, <span class="keyword">new</span> AuthFailureError(networkResponse));</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="comment">// <span class="doctag">TODO:</span> Only throw ServerError for 5xx status codes.</span></span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> ServerError(networkResponse);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> NetworkError(networkResponse);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>第 11 行，执行了 addCacheHeaders 方法，因为执行网络请求分两种情况，第一种：全新的请求；第二种：超时的缓存，也就是说缓存过期了，再次请求时需要附带上一次请求所带的<code>If-None-Match</code>或者<code>If-Modified-Since</code>等信息（如果有的话），如果是全新的请求，则没有这些；</p>
<p>第 12 行，利用 HttpStack 执行其 performRequest 方法，这是网络请求的核心。关于 HttpStack，请参考中的讲解；</p>
<p>第 13、14 行获取执行网络请求后的状态行和状态码</p>
<p>第 16 行，将 12 行中执行的网络请求的 响应的头部保存到一个 map 中，作为响应头</p>
<p>第 18 行，首先做一个特殊的处理，如果状态码是 304，也就是表明当前缓存依然可用，那么直接从缓存中读出数据，然后封装成 NetworkResponse，直接返回</p>
<p>第 24 行，判断响应中的响应体是否为空，因为例如像 204 表示响应执行成功，但没有数据返回，浏览器不用刷新，不用导向新页面，所以我们为 response 的内容添加一个长度为 0 的字节数组；否则读取响应中的响应体，将其转换为字节数组，然后赋给 responseContents</p>
<p>第 39 行，最终返回由 statusCode, responseContents, responseHeaders 组成的 NetworkResponse。</p>
<p><strong>注意：第 36 行，这里判断了如果响应码是在 200 范围之外的，直接抛出了 IOException，除了在 18 行处理的 304，其它非 2XX 的响应均不能处理，这也是 Volley 的一个缺陷。</strong></p>
<h2 id="关于-HttpStack"><a href="#关于-HttpStack" class="headerlink" title="关于 HttpStack"></a>关于 HttpStack</h2><p>这是一个接口，该接口中只有一个方法 performRequest，该方法的作用是：根据所给的参数，执行 HTTP 请求，并返回服务器的响应。</p>
<p>这个接口有两个实现类：HurlStack 和 HttpClientStack；</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">HttpStack</span> </span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Performs an HTTP request with the given parameters.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * &lt;p&gt;A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise,</span></span><br><span class="line"><span class="comment">     * and the Content-Type header is set to request.getPostBodyContentType().&lt;/p&gt;</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> request the request to perform</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> additionalHeaders additional headers to be sent together with</span></span><br><span class="line"><span class="comment">     *         &#123;<span class="doctag">@link</span> Request#getHeaders()&#125;</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> the HTTP response</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> HttpResponse <span class="title">performRequest</span><span class="params">(Request&lt;?&gt; request, Map&lt;String, String&gt; additionalHeaders)</span></span></span><br><span class="line"><span class="function">        <span class="keyword">throws</span> IOException, AuthFailureError</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>我们知道在 Android 中主要提供了两种方式来进行 HTTP 操作，分别是：HttpURLConnection 和 HttpClient。而 Volley 作为一个网络请求库，其底层网络请求的实现正是依靠这两种实现方式，所以 HttpStack 的两个实现类中，HurlStack 是基于 HttpURLConnection ，而 HttpClientStack 则基于 HttpClient 。不过 HttpURLConnection 和 HttpClient 的用法还是稍微有些复杂的，如果不进行适当封装的话，很容易就会写出不少重复代码。那么接下来看一下 HurlStack 和 HttpClientStack 是如何对 HttpURLConnection 和 HttpClient 进行封装的，这里选 HurlStack 类，主要看其如何实现 HttpStack 中的 performRequest 方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> HttpResponse <span class="title">performRequest</span><span class="params">(Request&lt;?&gt; request, Map&lt;String, String&gt; additionalHeaders)</span></span></span><br><span class="line"><span class="function">        <span class="keyword">throws</span> IOException, AuthFailureError </span>&#123;</span><br><span class="line">    String url = request.getUrl();</span><br><span class="line">    HashMap&lt;String, String&gt; map = <span class="keyword">new</span> HashMap&lt;String, String&gt;();</span><br><span class="line">    map.putAll(request.getHeaders());</span><br><span class="line">    map.putAll(additionalHeaders);</span><br><span class="line">    <span class="keyword">if</span> (mUrlRewriter != <span class="keyword">null</span>) &#123;</span><br><span class="line">        String rewritten = mUrlRewriter.rewriteUrl(url);</span><br><span class="line">        <span class="keyword">if</span> (rewritten == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IOException(<span class="string">"URL blocked by rewriter: "</span> + url);</span><br><span class="line">        &#125;</span><br><span class="line">        url = rewritten;</span><br><span class="line">    &#125;</span><br><span class="line">    URL parsedUrl = <span class="keyword">new</span> URL(url);</span><br><span class="line">    HttpURLConnection connection = openConnection(parsedUrl, request);</span><br><span class="line">    <span class="keyword">for</span> (String headerName : map.keySet()) &#123;</span><br><span class="line">        connection.addRequestProperty(headerName, map.get(headerName));</span><br><span class="line">    &#125;</span><br><span class="line">    setConnectionParametersForRequest(connection, request);</span><br><span class="line">    <span class="comment">// Initialize HttpResponse with data from the HttpURLConnection.</span></span><br><span class="line">    ProtocolVersion protocolVersion = <span class="keyword">new</span> ProtocolVersion(<span class="string">"HTTP"</span>, <span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line">    <span class="keyword">int</span> responseCode = connection.getResponseCode();</span><br><span class="line">    <span class="keyword">if</span> (responseCode == -<span class="number">1</span>) &#123;</span><br><span class="line">        <span class="comment">// -1 is returned by getResponseCode() if the response code could not be retrieved.</span></span><br><span class="line">        <span class="comment">// Signal to the caller that something was wrong with the connection.</span></span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IOException(<span class="string">"Could not retrieve response code from HttpUrlConnection."</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    StatusLine responseStatus = <span class="keyword">new</span> BasicStatusLine(protocolVersion,</span><br><span class="line">            connection.getResponseCode(), connection.getResponseMessage());</span><br><span class="line">    BasicHttpResponse response = <span class="keyword">new</span> BasicHttpResponse(responseStatus);</span><br><span class="line">    response.setEntity(entityFromConnection(connection));</span><br><span class="line">    <span class="keyword">for</span> (Entry&lt;String, List&lt;String&gt;&gt; header : connection.getHeaderFields().entrySet()) &#123;</span><br><span class="line">        <span class="keyword">if</span> (header.getKey() != <span class="keyword">null</span>) &#123;</span><br><span class="line">            Header h = <span class="keyword">new</span> BasicHeader(header.getKey(), header.getValue().get(<span class="number">0</span>));</span><br><span class="line">            response.addHeader(h);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> response;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>第 4 行，拿到请求中的 url 字符串</p>
<p>第 5 行，创建一个 Map，并将 request 中的 headers 都添加到该 map 中，（我们在构造 request 的时候，可以重写其中的 getHeaders 方法，在内部创建一个 Map，然后把 HTTP 请求中需要额外定制的 header 添加到该 map 中），同时把 performRequest 方法中的第二个参数 additionalHeaders 中的所有键值对也都添加到 map 中。</p>
<p>第 16 行，创建了 HttpURLConnection，这里设置了连接超时时间，读取数据的超时时间，如果我们使用的是 HTTPS 协议，那么还要对该连接设置 SSL 相关的属性</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> HttpURLConnection <span class="title">openConnection</span><span class="params">(URL url, Request&lt;?&gt; request)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">    HttpURLConnection connection = createConnection(url);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> timeoutMs = request.getTimeoutMs();</span><br><span class="line">    connection.setConnectTimeout(timeoutMs);</span><br><span class="line">    connection.setReadTimeout(timeoutMs);</span><br><span class="line">    connection.setUseCaches(<span class="keyword">false</span>);</span><br><span class="line">    connection.setDoInput(<span class="keyword">true</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// use caller-provided custom SslSocketFactory, if any, for HTTPS</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="string">"https"</span>.equals(url.getProtocol()) &amp;&amp; mSslSocketFactory != <span class="keyword">null</span>) &#123;</span><br><span class="line">        ((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> connection;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>第 17 行，将我们之前第 5 行的 map 中所有的 HTTP 头部设置添加到连接中。</p>
<p>第 20 行，设置 HTTP 的请求方法。如果我们在创建 request 时没有设置请求方法，那么需要根据该 request 中是否包含 body，如果包含，则设置为 post，如果不包含，则设置为 get。但是在创建 request 时不设置请求方法，是极为不负责的形式，我们在编码时不要这么做。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SuppressWarnings</span>(<span class="string">"deprecation"</span>)</span><br><span class="line"><span class="comment">/* package */</span> <span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">setConnectionParametersForRequest</span><span class="params">(HttpURLConnection connection,</span></span></span><br><span class="line"><span class="function"><span class="params">		Request&lt;?&gt; request)</span> <span class="keyword">throws</span> IOException, AuthFailureError </span>&#123;</span><br><span class="line">	<span class="keyword">switch</span> (request.getMethod()) &#123;</span><br><span class="line">		<span class="keyword">case</span> Method.DEPRECATED_GET_OR_POST:</span><br><span class="line">			<span class="comment">// This is the deprecated way that needs to be handled for backwards compatibility.</span></span><br><span class="line">			<span class="comment">// If the request's post body is null, then the assumption is that the request is</span></span><br><span class="line">			<span class="comment">// GET.  Otherwise, it is assumed that the request is a POST.</span></span><br><span class="line">			<span class="keyword">byte</span>[] postBody = request.getPostBody();</span><br><span class="line">			<span class="keyword">if</span> (postBody != <span class="keyword">null</span>) &#123;</span><br><span class="line">				<span class="comment">// Prepare output. There is no need to set Content-Length explicitly,</span></span><br><span class="line">				<span class="comment">// since this is handled by HttpURLConnection using the size of the prepared</span></span><br><span class="line">				<span class="comment">// output stream.</span></span><br><span class="line">				connection.setDoOutput(<span class="keyword">true</span>);</span><br><span class="line">				connection.setRequestMethod(<span class="string">"POST"</span>);</span><br><span class="line">				connection.addRequestProperty(HEADER_CONTENT_TYPE,</span><br><span class="line">						request.getPostBodyContentType());</span><br><span class="line">				DataOutputStream out = <span class="keyword">new</span> DataOutputStream(connection.getOutputStream());</span><br><span class="line">				out.write(postBody);</span><br><span class="line">				out.close();</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">break</span>;</span><br><span class="line">		<span class="keyword">case</span> Method.GET:</span><br><span class="line">			<span class="comment">// Not necessary to set the request method because connection defaults to GET but</span></span><br><span class="line">			<span class="comment">// being explicit here.</span></span><br><span class="line">			connection.setRequestMethod(<span class="string">"GET"</span>);</span><br><span class="line">			<span class="keyword">break</span>;</span><br><span class="line">		<span class="keyword">case</span> Method.DELETE:</span><br><span class="line">			connection.setRequestMethod(<span class="string">"DELETE"</span>);</span><br><span class="line">			<span class="keyword">break</span>;</span><br><span class="line">		<span class="keyword">case</span> Method.POST:</span><br><span class="line">			connection.setRequestMethod(<span class="string">"POST"</span>);</span><br><span class="line">			addBodyIfExists(connection, request);</span><br><span class="line">			<span class="keyword">break</span>;</span><br><span class="line">		<span class="keyword">case</span> Method.PUT:</span><br><span class="line">			connection.setRequestMethod(<span class="string">"PUT"</span>);</span><br><span class="line">			addBodyIfExists(connection, request);</span><br><span class="line">			<span class="keyword">break</span>;</span><br><span class="line">		<span class="keyword">default</span>:</span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(<span class="string">"Unknown method type."</span>);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里看一下其中的 post 方法，除了设置请求方法为 post 之外，还要把请求体添加上去，这就用到了 addBodyIfExists 方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">addBodyIfExists</span><span class="params">(HttpURLConnection connection, Request&lt;?&gt; request)</span></span></span><br><span class="line"><span class="function">		<span class="keyword">throws</span> IOException, AuthFailureError </span>&#123;</span><br><span class="line">	<span class="keyword">byte</span>[] body = request.getBody();</span><br><span class="line">	<span class="keyword">if</span> (body != <span class="keyword">null</span>) &#123;</span><br><span class="line">		connection.setDoOutput(<span class="keyword">true</span>);</span><br><span class="line">		connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());</span><br><span class="line">		DataOutputStream out = <span class="keyword">new</span> DataOutputStream(connection.getOutputStream());</span><br><span class="line">		out.write(body);</span><br><span class="line">		out.close();</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里先获取 request 中的 body（字节数组），如果 body 不为空，那么为连接添加”Content-Type”的头部，其值默认为“application/x-www-form-urlencoded”，默认编码方式为”UTF-8”。拼接起来一项请求头是：”Content-Type:application/x-www-form-urlencoded; charset=UTF-8”。</p>
<p>（如果值为：x-www-form-urlencoded，那么其请求体中的数据采用的是键值对，形如：key1=val1&amp;key2=val2），关于 HTTP 相关知识，请参考<a href>HTTP 包结构</a></p>
<p>额，刚刚扯得有点远了，再回到 performRequest 方法中，前面是阐述了一个 HTTP 的<strong>request</strong>是如何构造的，那么接下来就该执行 HTTP 请求了</p>
<p>第 23 行，这是一个阻塞的方法，一直等待服务器的响应。当然我们在 request 中已经设置了等待时间。</p>
<p>第 29 行，拿到 HTTP 的状态行和响应体，最终包装成 BasicHttpResponse；</p>
<p>第 33 行，拿到响应头部，应该是一个以 key-value 形式的 map，一次解析出响应头，添加到 response 中，最终返回。</p>
<h1 id="NetworkDispatcher-线程"><a href="#NetworkDispatcher-线程" class="headerlink" title="NetworkDispatcher 线程"></a>NetworkDispatcher 线程</h1><h2 id="构造方法"><a href="#构造方法" class="headerlink" title="构造方法"></a>构造方法</h2><p>首先看一下 NetworkDispatcher 的构造方法，传入了在 RequestQueue 中创建的 mNetworkQueue, mNetwork, mCache, mDelivery，其中 mNetwork 是 RequestQueue 的构造方法中传入的第二个参数 Network；mCache 是在 RequestQueue 的构造方法中传入的第一个参数 Cache，mDelivery 用来将子线程中得到的响应发送要主线程。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">NetworkDispatcher</span><span class="params">(BlockingQueue&lt;Request&gt; queue,</span></span></span><br><span class="line"><span class="function"><span class="params">        Network network, Cache cache,</span></span></span><br><span class="line"><span class="function"><span class="params">        ResponseDelivery delivery)</span> </span>&#123;</span><br><span class="line">    mQueue = queue;</span><br><span class="line">    mNetwork = network;</span><br><span class="line">    mCache = cache;</span><br><span class="line">    mDelivery = delivery;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="run-方法"><a href="#run-方法" class="headerlink" title="run 方法"></a>run 方法</h2><p>接下来看一下 NetworkDispatcher 线程的 run 方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">	Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);</span><br><span class="line">	Request request;</span><br><span class="line">	<span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			<span class="comment">// Take a request from the queue.</span></span><br><span class="line">			request = mQueue.take();</span><br><span class="line">		&#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">			<span class="comment">// We may have been interrupted because it was time to quit.</span></span><br><span class="line">			<span class="keyword">if</span> (mQuit) &#123;</span><br><span class="line">				<span class="keyword">return</span>;</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">continue</span>;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			request.addMarker(<span class="string">"network-queue-take"</span>);</span><br><span class="line"></span><br><span class="line">			<span class="comment">// If the request was cancelled already, do not perform the</span></span><br><span class="line">			<span class="comment">// network request.</span></span><br><span class="line">			<span class="keyword">if</span> (request.isCanceled()) &#123;</span><br><span class="line">				request.finish(<span class="string">"network-discard-cancelled"</span>);</span><br><span class="line">				<span class="keyword">continue</span>;</span><br><span class="line">			&#125;</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Tag the request (if API &gt;= 14)</span></span><br><span class="line">			<span class="keyword">if</span> (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.ICE_CREAM_SANDWICH) &#123;</span><br><span class="line">				TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());</span><br><span class="line">			&#125;</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Perform the network request.</span></span><br><span class="line">			NetworkResponse networkResponse = mNetwork.performRequest(request);</span><br><span class="line">			request.addMarker(<span class="string">"network-http-complete"</span>);</span><br><span class="line"></span><br><span class="line">			<span class="comment">// If the server returned 304 AND we delivered a response already,</span></span><br><span class="line">			<span class="comment">// we're done -- don't deliver a second identical response.</span></span><br><span class="line">			<span class="keyword">if</span> (networkResponse.notModified &amp;&amp; request.hasHadResponseDelivered()) &#123;</span><br><span class="line">				request.finish(<span class="string">"not-modified"</span>);</span><br><span class="line">				<span class="keyword">continue</span>;</span><br><span class="line">			&#125;</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Parse the response here on the worker thread.</span></span><br><span class="line">			Response&lt;?&gt; response = request.parseNetworkResponse(networkResponse);</span><br><span class="line">			request.addMarker(<span class="string">"network-parse-complete"</span>);</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Write to cache if applicable.</span></span><br><span class="line">			<span class="comment">// <span class="doctag">TODO:</span> Only update cache metadata instead of entire record for 304s.</span></span><br><span class="line">			<span class="keyword">if</span> (request.shouldCache() &amp;&amp; response.cacheEntry != <span class="keyword">null</span>) &#123;</span><br><span class="line">				mCache.put(request.getCacheKey(), response.cacheEntry);</span><br><span class="line">				request.addMarker(<span class="string">"network-cache-written"</span>);</span><br><span class="line">			&#125;</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Post the response back.</span></span><br><span class="line">			request.markDelivered();</span><br><span class="line">			mDelivery.postResponse(request, response);</span><br><span class="line">		&#125; <span class="keyword">catch</span> (VolleyError volleyError) &#123;</span><br><span class="line">			parseAndDeliverNetworkError(request, volleyError);</span><br><span class="line">		&#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">			VolleyLog.e(e, <span class="string">"Unhandled exception %s"</span>, e.toString());</span><br><span class="line">			mDelivery.postError(request, <span class="keyword">new</span> VolleyError(e));</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>第 3 行，设置当前线程的优先级为：Process.THREAD_PRIORITY_BACKGROUND，后台线程，相对来说优先级还是比较低的。</p>
<p>第 5 行，可以看到该线程是个死循环。</p>
<p>第 8 行，从 BlockingQueue 中取出一个 request，如果没有的话，就会被阻塞，也不会占用系统资源，赞。</p>
<p>第 22 行，如果该 request 还未被执行，但是在其它线程取消了，那么调用 request 的 finish 方法，其主要功能是将该 request 从 RequestQueue 的 mCurrentRequests 中移除掉当前 request。</p>
<p>第 33 行，如果能走到这一步，就表明真的要执行网络请求了，还记的 NetworkDispatcher 的构造方法中传入的 Network 对象吗，调用了它的 performRequest 方法，但是真正的执行者是 Network 的实现类 BasicNetwork。具体流程已经在上面进行了分析。</p>
<p>第 38 行，如果响应是 304 并且该 request 已经分发了，那么调用 request 的 finish 方法，该方法是将 RequestQueue 中 mWaitingRequests 队列中取出相同的 request，然后添加到 mCacheQueue 中。</p>
<p>第 44 行，调用 request 的 parseNetworkResponse 方法对 33 得到的 networkResponse 进行解析，至于具体怎么解析，还记的我们自定义请求时需要重写的方法，就是这个方法，所以具体如何解析 networkResponse 完全由你自己决定</p>
<p>第 49 行，执行缓存相关的操作，具体实现请参考<a href="https://zachaxy.github.io/2017/05/23/Volley%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-%E7%BC%93%E5%AD%98/">Volley 源码解析-缓存</a></p>
<p>第 55 行，将该 request 标记为已分发</p>
<p>第 56 行，将该 request 进行分发，调用了 mDelivery 的方法，这个 mDelivery 是在 NetworkDispatcher 的构造方法中传入进来的，具体创建是在 RequestQueue 的构造方法中，期创建方式是：<code>new ExecutorDelivery(new Handler(Looper.getMainLooper()))</code>看到这相信各位也能猜个大概了，我们前面也说了这个 ResponseDelivery 的作用是分发 response，我们在应用中请求网络数据，首先在子线程中执行网络请求，得到了响应之后，肯定要将结果返回到主线程，如何发呢？答案就是 Handler，而 ResponseDelivery 正是包装了一个 Handler；具体如何实现请参考<a href>Volley 源码解析-线程切换</a></p>
<h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>至此，一个完整的网络请求的过程就分析完了，这里在总结一下网络请求的过程：</p>
<p>需要执行网络的 request 无非是两种情况：</p>
<ul>
<li>第一种：一个新的请求，之前没有执行过，自然也没有对应的缓存，所以必须要走 NetworkDispatcher 这个线程；</li>
<li>第二种：之前执行过该请求，所以先从缓存中取，但是缓存不可用（缓存过期了或者不存在了），所以也必须要走 NetworkDispatcher 这个线程；</li>
</ul>
<p>走网络路线，有一个接口 Network，该接口内部封装了执行网络操作的所有方法，其唯一实现类是 BasicNetwork，而 BasicNetwork 中有包含了 HttpStack，HttpStack 接口的实现类才是真正实现了真正执行网络请求的功能（HttpURLConnection 和 HttpClient）</p>




    </div>










    <footer class="post-footer">








        <div class="post-eof"></div>

    </footer>
  </div>



  </article>



  </section>


  <nav class="pagination">
    <a class="extend prev" rel="prev" href="/page/6/"><i class="fa fa-angle-left"></i></a><a class="page-number" href="/">1</a><span class="space">&hellip;</span><a class="page-number" href="/page/6/">6</a><span class="page-number current">7</span><a class="page-number" href="/page/8/">8</a><span class="space">&hellip;</span><a class="page-number" href="/page/13/">13</a><a class="extend next" rel="next" href="/page/8/"><i class="fa fa-angle-right"></i></a>
  </nav>



          </div>





        </div>



  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">

      <div id="sidebar-dimmer"></div>

    <div class="sidebar-inner">





      <section class="site-overview-wrap sidebar-panel sidebar-panel-active">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">

              <img class="site-author-image" itemprop="image" src="/images/avatar.jpg" alt="zachaxy">

              <p class="site-author-name" itemprop="name">zachaxy</p>
              <p class="site-description motion-element" itemprop="description"></p>
          </div>

          <nav class="site-state motion-element">


              <div class="site-state-item site-state-posts">

                <a href="/archives">

                  <span class="site-state-item-count">64</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>







              <div class="site-state-item site-state-tags">
                <a href="/tags/index.html">
                  <span class="site-state-item-count">12</span>
                  <span class="site-state-item-name">标签</span>
                </a>
              </div>


          </nav>



          <div class="links-of-author motion-element">


                <span class="links-of-author-item">
                  <a href="https://github.com/zachaxy" target="_blank" title="GitHub">

                      <i class="fa fa-fw fa-globe"></i>GitHub</a>
                </span>

                <span class="links-of-author-item">
                  <a href="https://weibo.com/3242765540" target="_blank" title="Weibo">

                      <i class="fa fa-fw fa-globe"></i>Weibo</a>
                </span>


          </div>









        </div>
      </section>





    </div>
  </aside>



      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">zachaxy</span>


    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-area-chart"></i>
    </span>

      <span class="post-meta-item-text">站点文章字数总计&#58;</span>

    <span title="站点文章字数总计">147.9k</span>

</div>






  <div class="theme-info">主题 &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Muse</a></div>













      </div>
    </footer>


      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>

          <span id="scrollpercent"><span>0</span>%</span>

      </div>




  </div>



<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>
























    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>




    <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>




    <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>




    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>




    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>




    <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>






  <script type="text/javascript" src="/js/src/utils.js?v=5.1.3"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.3"></script>











  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.3"></script>







































  <script type="text/javascript">
    // Popup Window;
    var isfetched = false;
    var isXml = true;
    // Search DB path;
    var search_path = "search.xml";
    if (search_path.length === 0) {
      search_path = "search.xml";
    } else if (/json$/i.test(search_path)) {
      isXml = false;
    }
    var path = "/" + search_path;
    // monitor main search box;

    var onPopupClose = function (e) {
      $('.popup').hide();
      $('#local-search-input').val('');
      $('.search-result-list').remove();
      $('#no-result').remove();
      $(".local-search-pop-overlay").remove();
      $('body').css('overflow', '');
    }

    function proceedsearch() {
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay"></div>')
        .css('overflow', 'hidden');
      $('.search-popup-overlay').click(onPopupClose);
      $('.popup').toggle();
      var $localSearchInput = $('#local-search-input');
      $localSearchInput.attr("autocapitalize", "none");
      $localSearchInput.attr("autocorrect", "off");
      $localSearchInput.focus();
    }

    // search function;
    var searchFunc = function(path, search_id, content_id) {
      'use strict';

      // start loading animation
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay">' +
          '<div id="search-loading-icon">' +
          '<i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>' +
          '</div>' +
          '</div>')
        .css('overflow', 'hidden');
      $("#search-loading-icon").css('margin', '20% auto 0 auto').css('text-align', 'center');

      $.ajax({
        url: path,
        dataType: isXml ? "xml" : "json",
        async: true,
        success: function(res) {
          // get the contents from search data
          isfetched = true;
          $('.popup').detach().appendTo('.header-inner');
          var datas = isXml ? $("entry", res).map(function() {
            return {
              title: $("title", this).text(),
              content: $("content",this).text(),
              url: $("url" , this).text()
            };
          }).get() : res;
          var input = document.getElementById(search_id);
          var resultContent = document.getElementById(content_id);
          var inputEventFunction = function() {
            var searchText = input.value.trim().toLowerCase();
            var keywords = searchText.split(/[\s\-]+/);
            if (keywords.length > 1) {
              keywords.push(searchText);
            }
            var resultItems = [];
            if (searchText.length > 0) {
              // perform local searching
              datas.forEach(function(data) {
                var isMatch = false;
                var hitCount = 0;
                var searchTextCount = 0;
                var title = data.title.trim();
                var titleInLowerCase = title.toLowerCase();
                var content = data.content.trim().replace(/<[^>]+>/g,"");
                var contentInLowerCase = content.toLowerCase();
                var articleUrl = decodeURIComponent(data.url);
                var indexOfTitle = [];
                var indexOfContent = [];
                // only match articles with not empty titles
                if(title != '') {
                  keywords.forEach(function(keyword) {
                    function getIndexByWord(word, text, caseSensitive) {
                      var wordLen = word.length;
                      if (wordLen === 0) {
                        return [];
                      }
                      var startPosition = 0, position = [], index = [];
                      if (!caseSensitive) {
                        text = text.toLowerCase();
                        word = word.toLowerCase();
                      }
                      while ((position = text.indexOf(word, startPosition)) > -1) {
                        index.push({position: position, word: word});
                        startPosition = position + wordLen;
                      }
                      return index;
                    }

                    indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false));
                    indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false));
                  });
                  if (indexOfTitle.length > 0 || indexOfContent.length > 0) {
                    isMatch = true;
                    hitCount = indexOfTitle.length + indexOfContent.length;
                  }
                }

                // show search results

                if (isMatch) {
                  // sort index by position of keyword

                  [indexOfTitle, indexOfContent].forEach(function (index) {
                    index.sort(function (itemLeft, itemRight) {
                      if (itemRight.position !== itemLeft.position) {
                        return itemRight.position - itemLeft.position;
                      } else {
                        return itemLeft.word.length - itemRight.word.length;
                      }
                    });
                  });

                  // merge hits into slices

                  function mergeIntoSlice(text, start, end, index) {
                    var item = index[index.length - 1];
                    var position = item.position;
                    var word = item.word;
                    var hits = [];
                    var searchTextCountInSlice = 0;
                    while (position + word.length <= end && index.length != 0) {
                      if (word === searchText) {
                        searchTextCountInSlice++;
                      }
                      hits.push({position: position, length: word.length});
                      var wordEnd = position + word.length;

                      // move to next position of hit

                      index.pop();
                      while (index.length != 0) {
                        item = index[index.length - 1];
                        position = item.position;
                        word = item.word;
                        if (wordEnd > position) {
                          index.pop();
                        } else {
                          break;
                        }
                      }
                    }
                    searchTextCount += searchTextCountInSlice;
                    return {
                      hits: hits,
                      start: start,
                      end: end,
                      searchTextCount: searchTextCountInSlice
                    };
                  }

                  var slicesOfTitle = [];
                  if (indexOfTitle.length != 0) {
                    slicesOfTitle.push(mergeIntoSlice(title, 0, title.length, indexOfTitle));
                  }

                  var slicesOfContent = [];
                  while (indexOfContent.length != 0) {
                    var item = indexOfContent[indexOfContent.length - 1];
                    var position = item.position;
                    var word = item.word;
                    // cut out 100 characters
                    var start = position - 20;
                    var end = position + 80;
                    if(start < 0){
                      start = 0;
                    }
                    if (end < position + word.length) {
                      end = position + word.length;
                    }
                    if(end > content.length){
                      end = content.length;
                    }
                    slicesOfContent.push(mergeIntoSlice(content, start, end, indexOfContent));
                  }

                  // sort slices in content by search text's count and hits' count

                  slicesOfContent.sort(function (sliceLeft, sliceRight) {
                    if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) {
                      return sliceRight.searchTextCount - sliceLeft.searchTextCount;
                    } else if (sliceLeft.hits.length !== sliceRight.hits.length) {
                      return sliceRight.hits.length - sliceLeft.hits.length;
                    } else {
                      return sliceLeft.start - sliceRight.start;
                    }
                  });

                  // select top N slices in content

                  var upperBound = parseInt('1');
                  if (upperBound >= 0) {
                    slicesOfContent = slicesOfContent.slice(0, upperBound);
                  }

                  // highlight title and content

                  function highlightKeyword(text, slice) {
                    var result = '';
                    var prevEnd = slice.start;
                    slice.hits.forEach(function (hit) {
                      result += text.substring(prevEnd, hit.position);
                      var end = hit.position + hit.length;
                      result += '<b class="search-keyword">' + text.substring(hit.position, end) + '</b>';
                      prevEnd = end;
                    });
                    result += text.substring(prevEnd, slice.end);
                    return result;
                  }

                  var resultItem = '';

                  if (slicesOfTitle.length != 0) {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + highlightKeyword(title, slicesOfTitle[0]) + "</a>";
                  } else {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + title + "</a>";
                  }

                  slicesOfContent.forEach(function (slice) {
                    resultItem += "<a href='" + articleUrl + "'>" +
                      "<p class=\"search-result\">" + highlightKeyword(content, slice) +
                      "...</p>" + "</a>";
                  });

                  resultItem += "</li>";
                  resultItems.push({
                    item: resultItem,
                    searchTextCount: searchTextCount,
                    hitCount: hitCount,
                    id: resultItems.length
                  });
                }
              })
            };
            if (keywords.length === 1 && keywords[0] === "") {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>'
            } else if (resultItems.length === 0) {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>'
            } else {
              resultItems.sort(function (resultLeft, resultRight) {
                if (resultLeft.searchTextCount !== resultRight.searchTextCount) {
                  return resultRight.searchTextCount - resultLeft.searchTextCount;
                } else if (resultLeft.hitCount !== resultRight.hitCount) {
                  return resultRight.hitCount - resultLeft.hitCount;
                } else {
                  return resultRight.id - resultLeft.id;
                }
              });
              var searchResultList = '<ul class=\"search-result-list\">';
              resultItems.forEach(function (result) {
                searchResultList += result.item;
              })
              searchResultList += "</ul>";
              resultContent.innerHTML = searchResultList;
            }
          }

          if ('auto' === 'auto') {
            input.addEventListener('input', inputEventFunction);
          } else {
            $('.search-icon').click(inputEventFunction);
            input.addEventListener('keypress', function (event) {
              if (event.keyCode === 13) {
                inputEventFunction();
              }
            });
          }

          // remove loading animation
          $(".local-search-pop-overlay").remove();
          $('body').css('overflow', '');

          proceedsearch();
        }
      });
    }

    // handle and trigger popup window;
    $('.popup-trigger').click(function(e) {
      e.stopPropagation();
      if (isfetched === false) {
        searchFunc(path, 'local-search-input', 'local-search-result');
      } else {
        proceedsearch();
      };
    });

    $('.popup-btn-close').click(onPopupClose);
    $('.popup').click(function(e){
      e.stopPropagation();
    });
    $(document).on('keyup', function (event) {
      var shouldDismissSearchPopup = event.which === 27 &&
        $('.search-popup').is(':visible');
      if (shouldDismissSearchPopup) {
        onPopupClose();
      }
    });
  </script>

















  <script type="text/javascript" src="/js/src/js.cookie.js?v=5.1.3"></script>
  <script type="text/javascript" src="/js/src/scroll-cookie.js?v=5.1.3"></script>





</body>
</html>
